#include "libev_wrapper.h"

int32_t libev_init()
{
	/* use buddy allocator for libevent */
	#ifndef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
	LOG_ERROR("libev not support mem functions fail");
	return -1;
	#endif
	event_set_mem_functions(libev_malloc, libev_realloc, libev_free);

	/* pthread support */
	#ifndef EVTHREAD_USE_PTHREADS_IMPLEMENTED
	LOG_ERROR("libev not support pthread fail");
	return -1;
	#endif
	evthread_use_pthreads();

	/* customized log */
	event_set_log_callback(libev_log);

	/* fatal process */
	event_set_fatal_callback(libev_fatal);

	LOG_DEBUG("set libev complete, version:%s", event_get_version());
	return 0;
}

struct event_base* libev_init_base()
{
	/* use libevent base config */
	struct event_config* cfg = event_config_new();
	assert(cfg);

	/* support 01 feature and edge-trigger */
	if(event_config_require_features(cfg, EV_FEATURE_O1 | EV_FEATURE_ET) < 0)
		goto EVBASE_FAIL;

	/* if single thread, set no-lock */
	if(gCtx->cfg->multi_thread > 0)
		if(event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK) < 0)
			goto EVBASE_FAIL;
		
	/* epoll use changelist */
	if(event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST) < 0)
		goto EVBASE_FAIL;

	/* init libevent base */
	struct event_base* evbase = event_base_new_with_config(cfg);
	if(!evbase)
		goto EVBASE_FAIL;

	/* debug info */
	LOG_DEBUG("event base inited, method=%s", 
		event_base_get_method(evbase));

	event_config_free(cfg);
	return evbase;

EVBASE_FAIL:
	event_config_free(cfg);
	return NULL;
}

void libev_free_base(struct event_base* evbase)
{
	if(evbase)
		event_base_free(evbase);
}

void* libev_malloc(size_t nbytes)
{
	return buddy_alloc(gCtx->buddy, nbytes);
}

void* libev_realloc(void* ptr, size_t nbytes)
{
	return buddy_realloc(gCtx->buddy, ptr, nbytes);
}

void libev_free(void* ptr)
{
	buddy_free(gCtx->buddy, ptr);
}

void libev_log(int32_t severity, const char *msg)
{
	switch(severity)
	{
		case EVENT_LOG_DEBUG:
			LOG_DEBUG("%s", msg);
			break;
			
		case EVENT_LOG_MSG:
			LOG_TRACE("%s", msg);
			break;
			
		case EVENT_LOG_WARN:
			LOG_WARN("%s", msg);
			break;
			
		case EVENT_LOG_ERR:
			LOG_ERROR("%s", msg);
			break;
			
		default:
			LOG_FATAL("invalid libev log severity=%d", severity);
			break;
	}
}

void libev_fatal(int32_t err)
{
	LOG_FATAL("errno=%d, %s", err, strerror(err));

	if(gCtx->reactor)
	{
		char filename[64];
		struct timeval now; 
		event_base_gettimeofday_cached(gCtx->reactor, &now); 
		snprintf(filename, sizeof(filename), "eventdump.");
		get_timestamp_suffix(&now, filename + strlen(filename));
		FILE* file = fopen(filename, "w+");
		if(!file) return;
		event_base_dump_events(gCtx->reactor, file);
		fclose(file);
	}
	assert(0);
}

